//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<frame_control.cpp>
///	@path	~/src/engine/xcore/
///	@date	2007/12/02
///	@desc	Frame control of engine.

#include "config/config.h"

#include "engine/xcore/frame_control.h"

namespace xeres {

	// FrameControl
	FrameControl::FrameControl( void )
		: m_fpsLimit( 0 )
		, m_gapLimit( 0 )
		, m_timeLimit( 0 )
		, m_balanceCounter( 0 )
		, m_balanceTime( 0 )
		, m_balanceFps( 0 )
	{

	}

	// ~FrameControl
	FrameControl::~FrameControl( void )
	{

	}

	// FirstFrame
	void FrameControl::FirstFrame( uint superFrame )
	{
		m_updateCounter = Counter::Now();
		m_updateTime = 0;
		m_updateGap = 0;
		m_frameCounter = 0;
		m_subframeCounter = 0;
		m_superFrame = superFrame;
	}

	// UpdateFrame
	bool FrameControl::UpdateFrame( void )
	{
		// update counters
		Counter next = Counter::Now();
		Counter diff = ( next - m_updateCounter );
		m_updateTime = diff.ToTime();
		double gap = diff.ToSecond();

		if( m_fpsLimit > 0 && m_timeLimit > m_updateTime )
		{
			// under frame control

			m_updateGap = m_gapLimit;

			Time fillGap = m_updateTime;
			fillGap = m_timeLimit - fillGap;

			::Sleep( (DWORD)fillGap.ToMilliSecond() );

			next = Counter::Now();
			diff = ( next - m_updateCounter );
			m_updateTime = diff.ToTime();
			gap = diff.ToSecond();
		}

		m_updateCounter = next;
		m_updateGap = float(gap);
		m_updateFps = float(1.0 / gap);

		if( m_superFrame > 0 )
		{
			m_subframeCounter = m_frameCounter % m_superFrame;
		}
		else
		{
			m_subframeCounter = 0;
		}

		// frame statistics
		if( m_balanceCounter > 0 )
		{
			// need balance
			Time now = Time::Now();
			Time balance_diff = now - m_lastBalance;
			if( balance_diff < m_balanceTime )
			{
				++m_balanceCounter;
			}
			else
			{
				m_lastBalance = now;
				m_balanceFps = (float)(m_balanceCounter-1) / ( (float)( balance_diff.ToMilliSecond() ) / 1000 );
				m_balanceCounter = 1;
			}
			m_statFps = m_balanceFps;
		}
		else
		{
			m_statFps = m_updateFps;
		}

		UpdateTime();

		return true;
	}

	// DropFrame
	void FrameControl::SetFrameLimit( float fpsLimit )
	{
		if( fpsLimit == 0 )
		{
			m_fpsLimit = 0;
			m_gapLimit = 0;
			m_timeLimit = 0;
		}
		else
		{
			//TRACE_INFO( _S("Notice, parallel update is turned on.") );
			if( fpsLimit < 15 )
			{
				fpsLimit = 15;
			}
			m_fpsLimit = fpsLimit;
			m_gapLimit = 1.f / m_fpsLimit;
			m_timeLimit.FromMilliSecond( (int64)( m_gapLimit * 1000.f ) );
			m_renderUpdate = 0;
		}
	}

	// BalanceFrameRate
	void FrameControl::BalanceFrameRate( bool enable , const Time& period )
	{
		if( !enable )
		{
			m_balanceCounter = 0;
			m_balanceFps = 0;
			m_balanceTime = 0;
		}
		else
		{
			m_balanceCounter = 1;
			m_lastBalance = Time::Now();
			m_balanceTime = period;
			m_balanceFps = 0;
		}
	}

} // namespace xeres
